package cgs_worksheet

import (
	"cgs-server/helper"
	"cgs-server/server/model"
	"errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize/v2"
	uuid "github.com/satori/go.uuid"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"net/http"
	"strconv"
)

var (
	WsUpdateRow = "update_row"
	WsDeleteRow = "delete_row"
)

// 表格信息
type WorksheetInfo struct {
	// id
	Id primitive.ObjectID `json:"id" bson:"id"`
	// 数据库表名
	WsCollection string `json:"wsCollection,omitempty" bson:"wsCollection"`
	// 表格名称
	WsName string `json:"wsName" bson:"wsName"`
	// 字段
	Fields []*Field `json:"fields,omitempty" bson:"fields,omitempty"`
	// 配置信息
	Config string `json:"config,omitempty" bson:"config,omitempty"`
	// 创建时间
	//CreateTime		time.Time	`json:"createTime,omitempty" bson:"createTime,omitempty"`
	// 更新时间
	//UpdateTime		time.Time	`json:"updateTime,omitempty" bson:"updateTime,omitempty"`
	// 所属应用
	VisualData []string `json:"visualData,omitempty" bson:"visualData,omitempty"`
	VisualId   string   `json:"visualId" bson:"-"` // 应用 id

	model.TenantModel `json:",inline" bson:",inline"`
}

func (w *WorksheetInfo) SetId(id primitive.ObjectID) {
	w.Id = id
}

func (w *WorksheetInfo) GetId() string {
	return w.Id.Hex()
}

// WorksheetInfoVo 返回前端数据
type WorksheetInfoVo struct {
	// id
	Id primitive.ObjectID `json:"id" bson:"id"`
	// 表格名称
	WsName string `json:"wsName" bson:"wsName"`
	// 字段
	Fields []*Field `json:"fields,omitempty" bson:"fields,omitempty"`
}

// 表格字段信息
type Field struct {
	// 字段名
	Name string `json:"name" bson:"name"`
	// 别名，中文名
	Alia string `json:"alia,omitempty" bson:"alia,omitempty"`
	// 类型
	DataType interface{} `json:"dataType" bson:"dataType"`
	// 其他属性
	Options map[string]interface{} `json:"options,omitempty" bson:"options,omitempty"`
}

func (w *WorksheetInfo) ResolveDataType() {
	if w.Fields == nil || len(w.Fields) == 0 {
		return
	}
	for _, field := range w.Fields {
		flag := false
		for k, v := range DataTypeMap {
			if v.Desc == field.DataType.(string) {
				field.DataType = k
				flag = true
				break
			}
		}
		if !flag {
			field.DataType = 0 // 文本
		}
	}
}

func (w *WorksheetInfo) GetDataType(name string) DataType {
	if w.Fields != nil {
		for _, field := range w.Fields {
			if field.Name == name {
				switch dt := field.DataType.(type) {
				case DataType:
					return dt
				case int32:
					return DataType(dt)
				case int:
					return DataType(dt)
				default:
					return Text
				}
			}
		}
		return -1
	}

	return -1
}

func (w *WorksheetInfo) GetOptions(name string) map[string]interface{} {
	for _, field := range w.Fields {
		if field.Name == name {
			return field.Options
		}
	}

	return nil
}

// 过滤参数
type FilterParam struct {
	// 表id
	Id string `json:"id" bson:"id"`
	// 表格 id （新）
	WorksheetId primitive.ObjectID `json:"worksheetId" bson:"worksheetId" validate:"required"`
	// 数据库表名
	WsCollection string `json:"wsCollection" bson:"wsCollection"`
	// 过滤条件
	Filters []FilterSortInput `json:"filters,omitempty" bson:"filters,omitempty"`
	// 页码
	PageNo int64 `json:"pageNo,omitempty" bson:"pageNo,omitempty"`
	// 行数
	PageSize int64 `json:"pageSize,omitempty" bson:"pageSize,omitempty"`
	// 排序字段
	SortField string `json:"sortField,omitempty" bson:"sortField,omitempty"`
	// 是否升序
	IsAsc bool `json:"isAsc,omitempty" bson:"isAsc,omitempty"`
	// 搜索文本
	Keywords string `json:"keywords,omitempty" bson:"keywords,omitempty"`
	// appKey
	//AppKey string `json:"appKey" validate:"required"`
	// sign
	//Sign	string `json:"sign" validate:"required"`

	model.BaseModel
}

func (f *FilterParam) GetFilterParam(wsInfo *WorksheetInfo) bson.D {
	var spliceType string
	result := bson.D{}
	if wsInfo == nil {
		return result
	}

	if f.Filters != nil {
		arr := bson.A{}

		for i := 0; i < len(f.Filters); i++ {
			filterRow := f.Filters[i]

			// 连接类型
			if spliceType == "" {
				spliceType = filterRow.SpliceType.GetFilterExpression()
			}

			filter := filterRow.GenerateFilterParam(wsInfo)
			if filter != nil {
				arr = append(arr, filter)
			}
		}

		if len(arr) > 0 {
			result = bson.D{
				{spliceType, arr},
			}
		}
	}

	if f.Keywords != "" {
		// 遍历所有字段
		arr := bson.A{}
		for idx := range wsInfo.Fields {
			arr = append(arr, bson.M{
				wsInfo.Fields[idx].Name: bson.M{
					"$regex": f.Keywords,
				},
			})
		}
		resMap := result.Map()
		if resMap["$or"] != nil {
			if or, ok := resMap["$or"].(primitive.A); ok {
				or = append(or, arr...)
				result = append(result, bson.E{Key: "$or", Value: or})
			}
		} else {
			result = append(result, bson.E{Key: "$or", Value: arr})
		}
	}

	return result
}

// row data
type WsRowData struct {
	RowId       string                          `json:"rowId" bson:"rowId"`                                 // 行id
	WorksheetId primitive.ObjectID              `json:"worksheetId" bson:"worksheetId" validate:"required"` // 表单id
	Data        map[string]interface{}          `json:"data" bson:"-"`                                      // 数据
	RowData     `json:",inline" bson:",inline"` // 数据

	WsInfo *WorksheetInfo `json:"-" bson:"-"` // 表单

	model.TenantModel `json:",inline" bson:",inline"`
	//BaseParam	`json:",inline" bson:"-"`
}

// row data
type WsRowDataBatch struct {
	RowIds    []string `json:"rowIds" bson:"rowIds"` // 行id
	WsRowData `json:",inline"`
	IsAll     bool `json:"isAll"` // 是否选择了全部
	// 过滤条件
	Filters []FilterSortInput `json:"filters,omitempty" bson:"fields,omitempty"`
}

func (f *WsRowDataBatch) GetFilterParam(wsInfo *WorksheetInfo) bson.D {
	var spliceType string
	result := bson.D{}
	if wsInfo == nil {
		return result
	}

	if f.Filters != nil {
		arr := bson.A{}

		for i := 0; i < len(f.Filters); i++ {
			filterRow := f.Filters[i]

			// 连接类型
			if spliceType == "" {
				spliceType = filterRow.SpliceType.GetFilterExpression()
			}

			filter := filterRow.GenerateFilterParam(wsInfo)
			if filter != nil {
				arr = append(arr, filter)
			}
		}

		if len(arr) > 0 {
			result = bson.D{
				{spliceType, arr},
			}
		}
	}
	return result
}

// 批量根据过滤条件批量修改
type WsRowDataBatchByFilter struct {
	WorksheetId    primitive.ObjectID `json:"worksheetId" bson:"worksheetId" validate:"required"` // 表单id
	UpdateSections []WsRowDataBatch   `json:"updateSections"`
}

func (w *WsRowDataBatchByFilter) ResolveWsRowDataBatches() {
	for i := range w.UpdateSections {
		w.UpdateSections[i].IsAll = true
		w.UpdateSections[i].WorksheetId = w.WorksheetId
	}
}

// 带有 appKey, sign 参数的记录
type WsRowDataVo struct {
	WsRowData `json:",inline" bson:",inline"`
	//BaseParam `json:",inline" bson:"-"`
}

type RowData map[string]interface{}

func (w *WsRowData) SetId(id primitive.ObjectID) {
	w.RowId = id.Hex()
}

func (w *WsRowData) GetId() string {
	return w.RowId
}

func (w *WsRowData) TransferData() error {
	if w.Data == nil {
		return errors.New("row data is nil")
	}
	if w.WsInfo == nil {
		return errors.New("WorksheetInfo is nil")
	}
	if len(w.Data) == 0 {
		return errors.New("数据不能为空")
	}
	// 保存的数据，key-value
	doc := make(map[string]interface{}, len(w.Data))

	for key := range w.Data {
		dataType := w.WsInfo.GetDataType(key)
		options := w.WsInfo.GetOptions(key)
		data := w.Data[key]
		switch dataType {
		case Text:
			doc[key] = data
		case Num:
			if v, ok := data.(string); ok {
				number, err := strconv.ParseFloat(v, 64)
				if err != nil {
					return err
				}
				doc[key] = number
			} else {
				doc[key] = data
			}
		case DATE, TIME:
			if options != nil {
				format := options["format"].(string)
				timeStr := data.(string)
				t, _ := helper.StringToTime(timeStr, format)
				doc[key] = t
			} else {
				doc[key] = data
			}
		case -1:
		// 无此字段
		case UploadImg:
			if data == nil {
				doc[key] = []interface{}{}
				break
			}
			if imgArr, ok := data.([]interface{}); ok {
				for _, img := range imgArr {
					ig := img.(map[string]interface{})
					ig["type"] = "img"
					if ig["uid"] == nil {
						v4, err := uuid.NewV4()
						if err != nil {
							return err
						}
						ig["uid"] = v4.String()
					}
				}
			}
			doc[key] = data
		default:
			doc[key] = data
		}
	}

	w.RowData = doc
	return nil
}

// 批量新增记录
type BatchAddRowVo struct {
	//BaseParam `json:",inline" bson:"-"`
	WorksheetId primitive.ObjectID       `json:"worksheetId" bson:"worksheetId" validate:"required"` // 表单id
	Rows        []map[string]interface{} `json:"rows" bson:"-"`

	WsInfo *WorksheetInfo `json:"-" bson:"-"` // 表单
}

func (b *BatchAddRowVo) GetWsRowDataList() []*WsRowData {
	result := []*WsRowData{}
	if len(b.Rows) == 0 {
		return result
	}
	for idx := range b.Rows {
		result = append(result, &WsRowData{
			WorksheetId: b.WorksheetId,
			Data:        b.Rows[idx],
			WsInfo:      b.WsInfo,
		})
	}
	return result
}

// 批量删除记录
type BatchDeleteRowVo struct {
	WorksheetId primitive.ObjectID `json:"worksheetId" bson:"-"`
	RowIds      []string           `json:"rowIds" bson:"-"`
	WsInfo      *WorksheetInfo     `json:"-" bson:"-"`     // 表单
	IsAll       bool               `json:"isAll" bson:"-"` // 是否全选

	model.TenantModel `json:",inline" bson:",inline"`
}

// 导入表格数据
type ImportExcelVo struct {
	Filepath    string                 `json:"filepath" bson:"-"`
	RowNumber   int                    `json:"rowNumber"`
	SheetNumber int                    `json:"sheetNumber"`
	Values      map[string]interface{} `json:"values"` // 字段没有选对应的列时，列数为nil
	RowList     []*WsRowData           // 批量添加到数据库的数据
	WorksheetId primitive.ObjectID     `json:"worksheetId"`

	Errors []ImportError // 导入数据时的错误信息

	WsInfo *WorksheetInfo `json:"-" bson:"-"` // 表单
}

// 处理表格数据
func (iev *ImportExcelVo) DealWorksheetData() error {
	if iev.Filepath == "" {
		return errors.New("filepath is empty")
	}
	if iev.WsInfo == nil {
		return errors.New("worksheetInfo is nil")
	}
	resp, err := http.Get(iev.Filepath)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return fmt.Errorf("http status: %v %v", resp.StatusCode, resp.Status)
	}
	defer resp.Body.Close()

	f, err := excelize.OpenReader(resp.Body)
	if err != nil {
		return err
	}

	rowNumber := iev.RowNumber
	values := iev.Values

	// 获取 Sheet1 上第一行的单元格
	rows, err := f.GetRows("Sheet1")
	if len(rows) == 0 || rows[rowNumber] == nil {
		return errors.New("worksheet is empty")
	}

	fieldsData := []*WsRowData{}
	errorData := []ImportError{}
	for rowIndex, row := range rows {
		if rowIndex <= rowNumber {
			continue
		}
		// 一条记录的数据
		fieldData := bson.M{}
		// 默认值都设置为 nil
		for _, field := range iev.WsInfo.Fields {
			fieldData[field.Name] = nil
		}
		for colIndex, colCell := range row {
			if colCell == "" {
				continue
			}
			for key, value := range values {
				if _, ok := value.(string); ok {
					continue
				}
				if v, ok := value.(float64); ok {
					if colIndex == int(v) {
						// 判断数据类型
						dataType := iev.WsInfo.GetDataType(key)
						options := iev.WsInfo.GetOptions(key)

						switch dataType {
						case Text:
							fieldData[key] = colCell
						case Num:
							i, err := strconv.ParseFloat(colCell, 64)
							// 数字精度
							if options != nil && options["precision"] != nil {
								precision := int(options["precision"].(float64))
								s := strconv.FormatFloat(i, 'f', precision, 64)
								i, err = strconv.ParseFloat(s, 64)
							}
							if err != nil {
								e := ImportError{
									RowNumber: rowIndex,
									ColNumber: colIndex,
								}
								e.Sting()
								errorData = append(errorData, e)
								continue
							}
							fieldData[key] = i
						case DATE, TIME:
							if options != nil {
								format := options["format"].(string)
								t, err := helper.ParseExcelDateTime(colCell, format)
								if err != nil {
									continue
								}
								fieldData[key] = t
							} else {
								fieldData[key] = colCell
							}
						default:
							fieldData[key] = colCell
						}
					}
				}
			}
		}

		// 判断是否全空
		flag := true
		for _, field := range iev.WsInfo.Fields {
			if fieldData[field.Name] != nil {
				if v, ok := fieldData[field.Name].(string); ok {
					if v != "" {
						flag = false
						break
					}
				} else {
					flag = false
					break
				}

			}
		}
		if flag {
			// 循环下一行
			continue
		}

		wsRowData := &WsRowData{
			WorksheetId: iev.WorksheetId,
			RowData:     RowData(fieldData),
			WsInfo:      iev.WsInfo,
		}
		fieldsData = append(fieldsData, wsRowData)
	}

	iev.RowList = fieldsData
	iev.Errors = errorData
	return nil
}

type BaseParam struct {
	//WorksheetId primitive.ObjectID `json:"worksheetId" bson:"worksheetId" validate:"required"`
	// appKey
	AppKey string `json:"appKey" validate:"required"`
	// sign
	Sign string `json:"sign" validate:"required"`
}

// ImportError 导入错误
type ImportError struct {
	RowNumber int    `json:"rowNumber" bson:"rowNumber"`
	ColNumber int    `json:"colNumber" bson:"colNumber"`
	ErrMsg    string `json:"errMsg" bson:"errMsg"`
}

func (e *ImportError) Sting() {
	e.ErrMsg = fmt.Sprintf("(第%d行) (第%d列) 导入数据格式错误", e.RowNumber+1, e.ColNumber+1)
}
